/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package controller;

import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.PopupMenu;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import model.hitoModel;
import model.roadModel;
import store.graphStore;
import type.hitoType;
import type.mouseType;
import view.layerView;

/**
 *
 * @author JorgeLuis
 */
public class layerController {
    
    private layerView       view            = new layerView();
    private graphStore      graph           = new graphStore();
    private String          state           = "road";
    private String          lastState       = this.state;
    private hitoModel       tempHito        = null;
    private roadModel       tempRoad        = null;
    private mainController  mainController;
    private hitoModel       hitoMove;
    private Point           lastPoint       = null;
    private int             id              = -1;
    private int             parentId        = -1;
    
    private String          textMenuEdit    = "edit element";
    private String          textMenuDelete  = "delete element";
    

    public layerController(int id, int parentId) {
        this.view.controller(this);
        this.id         = id;
        this.parentId   = parentId;
    }
    
    public void mainController(mainController aThis) {
        this.mainController = aThis;
    }

    public void drawn(Graphics2D g2) {
        
        if(this.tempHito != null){
            this.tempHito.drawn(g2);
        }
        
        if(this.tempRoad != null){
            this.tempRoad.drawn(g2);
            this.tempRoad.getHitoOne().drawn(g2);
            this.tempRoad.getHitoTwo().drawn(g2);
        }
        
        ArrayList<hitoModel> listHitos = graph.getHitos();
        for (int i = 0; i < listHitos.size(); i++) {
            
            listHitos.get(i).drawn(g2);
        }
        
        ArrayList<roadModel> listRoads = graph.getRoads();
        for (int i = 0; i < listRoads.size(); i++) {
            
            listRoads.get(i).drawn(g2);
        }
    }

    private void drawPoint(Point point) {
        
        if(tempHito == null){
            this.tempHito = new hitoModel(point);            
            this.tempHito.setType(hitoType.HITO);
        }
        this.tempHito.setPosition(point);       
        
        view.repaint();                             
    }
    
    private hitoModel addPoint(Point point){
        
        // rugles
        
        hitoModel existHito = graph.findHito(point);
        
        //* se valida que el punto no exista un hito o area         
        if(existHito != null){            
            System.out.println("No se puede colocar porque se cruza con un HITO");
            return null;
        }
        
        hitoModel newHito = new hitoModel(point);       // creamos un nuevo hito
        newHito.setType(hitoType.HITO);                 // de tipo punto (HITO)
        newHito.setId(this.mainController.assignId());  // le asignamos un id
        graph.addHito(newHito);                         // lo adicionamos al grafo
        
        //* se busca si en el punto se cruza con un camino (Road)
        roadModel existRoad = graph.findRoad(point);
        
        //* si existe un camino en que se cruza se hace divide el camino y se coloca el punto        
        if(existRoad != null){                      
            putHitoInRoad(newHito, existRoad);
        }
//        System.out.println(" creacion del hito con id: "+newHito.getId());
        return newHito;
    }
    
    public layerView getView() {
        return view;
    }

    private void drawRoad(Point point) {
        
        if(this.tempRoad == null ){
            this.tempHito = null;
            this.tempRoad = new roadModel();
            hitoModel tempHitoOne = new hitoModel(point);
            hitoModel tempHitoTwo = new hitoModel(point);
            
            tempHitoOne.setType(hitoType.HITO);
            tempHitoTwo.setType(hitoType.HITO);
            
            this.tempRoad.setHitoTwo(tempHitoTwo);
            this.tempRoad.setHitoOne(tempHitoOne);
            
            this.tempRoad.setPositionOne(point);
            this.tempRoad.setPositionTwo(point);
        }
        
        else {
            
            hitoModel tempHitoTwo = tempRoad.getHitoTwo();
            tempHitoTwo.setPosition(point);
            this.tempRoad.setPositionTwo(point);
        }
        view.repaint();
    }

    private roadModel addRoad(Point pointOne, Point pointTwo) {
        
        //* crea el nuevo Camino
        roadModel newRoad = new roadModel();
        
        //* busca si existe en el punto uno algun hito
        hitoModel existHito = graph.findHito(pointOne);
        
        //* valida si existe algun hito si existe se une a el, de lo contrario crea un hito.
        if(existHito == null){
            existHito = addPoint(pointOne);
//            roadModel existRoad = graph.findRoad(pointOne);
        }
        
        newRoad.setHitoOne(existHito);      //* al nuevo camino le adicionamos el hito encontrado o creado
        existHito.addRoad(newRoad);         //* al hito le adicionamos el nuevo camino
        
        //* validamos si es un tipo HITO para que el extremo del camino se ubique en el centro de hito
        if(existHito.getType() == hitoType.HITO){
            pointOne = new Point(existHito.getPosition());
        }
        newRoad.setPositionOne(pointOne);                    
        
        
        //* busca el si existe en el punto uno algun hito
        existHito = graph.findHito(pointTwo);
        
        //* valida si existe algun hito si existe se une a el, de lo contrario crea un hito.
        if(existHito == null){
            existHito = addPoint(pointTwo);
//            roadModel existRoad = graph.findRoad(pointTwo);
        }
        
        newRoad.setHitoTwo(existHito);      //* al nuevo camino le adicionamos el hito encontrado o creado
        existHito.addRoad(newRoad);         //* al hito le adicionamos el nuevo camino
        
        //* validamos si es un tipo HITO para que el extremo del camino se ubique en el centro de hito
        if(existHito.getType() == hitoType.HITO){
            pointTwo = new Point(existHito.getPosition());
        }
        newRoad.setPositionTwo(pointTwo);
        
        if(newRoad.getHitoOne().getArea().intersects(newRoad.getHitoTwo().getArea().getBounds())){
            System.out.println("no de puede crear porque se interceta con el mismo camino");
            return null;
        }
        newRoad.setId(this.mainController.assignId());
        graph.addRoad(newRoad);
        this.tempRoad = null;
        System.out.println(" creacion del nuevo camino id: "+newRoad.getId());
        return newRoad;
    }
    
    public void putHitoInRoad(hitoModel hito, roadModel road){           
        
        //* removemos el camino del grafo        
        road.getHitoOne().removeRoad(road.getId());        
        
        //* nuevo camino
        roadModel newRoad = new roadModel();      
        
        //* asignamos el camino al hito y el hito al camino
        newRoad.setPositionOne(new Point(road.getPositionOne()));
        newRoad.setHitoOne(road.getHitoOne());
        road.getHitoOne().addRoad(newRoad);
        
        
        //* assign point to new road
        newRoad.setPositionTwo(new Point(hito.getPosition()));
        newRoad.setHitoTwo(hito);
        hito.addRoad(newRoad);
        
        //* assign point to road
        road.setPositionOne(new Point(hito.getPosition()));
        road.setHitoOne(hito);
        hito.addRoad(road);
        
        graph.addRoad(newRoad);
    }    

    private void drawArea(Point point, boolean changeLastPoint) {
        
        if(this.tempHito == null){
            
            this.tempHito = new hitoModel(point);
            this.tempHito.setType(hitoType.AREA);
            this.tempHito.getArea().reset();
            this.tempHito.getArea().addPoint(point.x, point.y);
            this.tempHito.getArea().addPoint(point.x, point.y);
        }
        
        else if(this.tempHito != null && !changeLastPoint){
            tempHito.getArea().xpoints[tempHito.getArea().npoints-1] = (int) point.getX();
            tempHito.getArea().ypoints[tempHito.getArea().npoints-1] = (int) point.getY();
        }
        else if(changeLastPoint && this.tempHito != null){
            this.tempHito.getArea().addPoint(point.x, point.y);
        }
        view.repaint();
    }

    private void addArea(Polygon area) {
        
        if(area.npoints <= 2){
            return;
        }
        
        for (int i = 0; i < graph.getHitos().size(); i++) {
            if(intersectsAreas(area, graph.getHitos().get(i).getArea())){
                return;                
            }
        }
        hitoModel newArea = new hitoModel(new Point((int)area.getBounds().getCenterX(),(int) area.getBounds().getCenterY()));
        newArea.setArea(new Polygon(area.xpoints, area.ypoints, area.npoints));
        newArea.setType(hitoType.AREA);
        this.graph.addHito(newArea);
    }
    
    public boolean intersectsAreas(Polygon area1, Polygon area2){
        
        if (area1.getBounds2D().intersects(area2.getBounds2D())) {
            
            for (int i = 0; i < area1.npoints; i++) {
                if(area2.intersects(area1.xpoints[i]-1.5, area1.ypoints[i]-1.5, 3, 3)){
                    
                    return true;
                }
            }
        
            for (int i = 0; i < area2.npoints; i++) {
                if(area1.intersects(area2.xpoints[i]-1.5, area2.ypoints[i]-1.5, 3, 3)){
                    
                    return true;
                }
            }
        }                
        return false;
    }
    
    public void action(mouseController mouse) {
        
//        System.out.println("mouse: "+mouse);
        this.lastState = this.state;
//        if (mouse.getState() == mouseType.mouseEntered){
//            this.state = this.lastState;
//        }
//        
//        else if(mouse.getState() == mouseType.mouseExited){
//            System.out.println("exits");
//            this.resetVariables();
//            this.state = "null";
//            this.view.repaint();
//            return ;
//        }
        
        
        selectObject(mouse.getPoint());
        switch(state){            
            case "point":                
                this.actionPoint(mouse);                
                break;
                
            case "road":                
                this.actionRoad(mouse);                
                break;
                
            case "area":                
                this.actionArea(mouse);                        
                break;
                
            case "select":                
                this.actionSelect(mouse);
                break;
                
            case "editArea":
                this.actionEditArea(mouse);
                break;                
        }             
        this.view.repaint();
    }
   
    public void  selectObject(Point point){
        ArrayList<hitoModel> listHitos = graph.getHitos();
        ArrayList<roadModel> listRoads = graph.getRoads();
        
        for (int i = 0; i < listHitos.size(); i++) {

            if(listHitos.get(i).intersects(point)){
                listHitos.get(i).setColorEdge(listHitos.get(i).getColorEdgeContrary());
            }
            else{
                listHitos.get(i).setColorEdge(listHitos.get(i).getColorEdgeOriginal());
            }
        }
        
        for (int i = 0; i < listRoads.size(); i++) {

            if(listRoads.get(i).intersects(point)){                
                listRoads.get(i).setColor(listRoads.get(i).getColorContrary());
            }
            else{
                listRoads.get(i).setColor(listRoads.get(i).getColorOriginal());
            }
        }
        this.view.repaint();
    }

    public void setState(String event) {
        this.state = event;
        this.lastState = event;
        this.resetVariables();
    }
    
    public void moveHito(Point point, hitoModel obj){
        
        
        if(this.lastPoint == null){
            this.lastPoint = point;
        }
        if(obj != null){                  
            int posX = obj.getPosition().x + (point.x - this.lastPoint.x );
            int posY = obj.getPosition().y + (point.y - this.lastPoint.y );
            Point changePoint = new Point(posX, posY);
            obj.setPosition(changePoint);
            this.lastPoint = point;
        }
        this.view.repaint();
        
        
    }
    
    public void resetVariables(){
        
        if(this.tempHito != null){
            this.tempHito.setSeeVertices(false);
        }
        this.tempHito = null;
        this.tempRoad = null;
        this.hitoMove   = null;
        this.lastPoint  = null;
        this.view.repaint();
    }   
    
    private void actionPoint(mouseController mouse) {
        this.drawPoint(mouse.getPoint());                
        if(mouse.getState() == mouseType.mouseClicked && mouse.isClickLeft() == true){
            mouse.setClickLeft(false);
            this.addPoint(mouse.getPoint());
        }
    }

    private void actionRoad(mouseController mouse) {
        if(this.tempRoad == null && mouse.getState() == mouseType.mouseMoved){
            this.drawPoint(mouse.getPoint());
        }

        else if(this.tempRoad == null && mouse.getState() == mouseType.mouseClicked && mouse.isClickLeft() == true){
            mouse.setClickLeft(false);
            this.drawRoad(mouse.getPoint());                    
        }

        else if(this.tempRoad != null && mouse.getState() == mouseType.mouseMoved){
            this.drawRoad(mouse.getPoint());
        }

        else if(this.tempRoad != null && mouse.getState() == mouseType.mouseClicked && mouse.isClickLeft() == true){
            mouse.setClickLeft(false);
            if(this.tempRoad.getHitoOne().getArea().intersects(this.tempRoad.getHitoTwo().getArea().getBounds())){
                return;
            }
            this.addRoad(new Point(this.tempRoad.getPositionOne()),new Point(this.tempRoad.getPositionTwo()));
        } 
}
    
    private void actionArea(mouseController mouse) {
        if(this.tempHito == null && mouse.getState() == mouseType.mouseClicked && mouse.isClickLeft() == true){
            mouse.setClickLeft(false);
            drawArea(mouse.getPoint(), true);
        }
        else if(this.tempHito != null && mouse.getState() == mouseType.mouseMoved ){                    
            drawArea(mouse.getPoint(), false);
        }
        else if(this.tempHito != null && mouse.getState() == mouseType.mouseClicked && mouse.isClickLeft() == true){
            mouse.setClickLeft(false);
            drawArea(mouse.getPoint(), true);
        }
        else if(this.tempHito != null && mouse.getState() == mouseType.mouseClicked && mouse.isClickRigth() == true){
            mouse.setClickRigth(false);
            addArea(this.tempHito.getArea());
            this.tempHito = null;
        }
    }

    private void actionSelect(mouseController mouse) {
        
        
        if( mouse.getState() == mouseType.mousePressed && mouse.isPressedLeft() && this.hitoMove == null ){
            this.hitoMove = graph.findHito(mouse.getPoint());
        }

        else if( mouse.getState() == mouseType.mouseDragged && mouse.isPressedLeft() && this.hitoMove != null ){
            this.moveHito(mouse.getPoint(), this.hitoMove);
        }
        
        else if(mouse.getState() == mouseType.mouseReleased && !mouse.isPressedLeft() && this.hitoMove != null){
            this.hitoMove   = null;
            this.lastPoint  = null;
        }
        
        else if( mouse.getState() == mouseType.mouseClicked && mouse.isClickRigth() && this.tempHito == null ){
            mouse.setClickRigth(false);
            this.tempHito = graph.findHito(mouse.getPoint());
            
            if(this.tempHito != null){
                System.out.println("seleciono el hito > "+ this.tempHito.getId());
                this.subMenu(mouse.getPoint());
            }
            else{
                this.tempRoad = graph.findRoad(mouse.getPoint());
            
                if(this.tempRoad != null){
                    System.out.println("seleciono el road > "+ this.tempRoad.getId());
                    this.subMenu(mouse.getPoint());
                }
            }
        }
    }

    private void subMenu(Point point) {
        
        PopupMenu subMenu = new PopupMenu();       
                 
        subMenu.add(this.textMenuEdit);
        subMenu.addSeparator();
        subMenu.add(this.textMenuDelete);        
        
        subMenu.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent ae) {
                
                if(tempHito != null){
                    actionHito(ae.getActionCommand());
                }   
                else if (tempRoad != null){
                    actionRoad(ae.getActionCommand());
                }
            }
            
        });
        
        this.view.add(subMenu);
        subMenu.show(view, point.x, point.y);
    }
    
    private void actionHito(String paramString) {
        
        if(this.textMenuEdit.equals(paramString)){
            hitoModel editHito = this.tempHito;
            this.mainController.editHito(editHito);
            this.tempHito = null;
        }
        
        else if(this.textMenuDelete.equals(paramString)){
            this.deleteHito(this.tempHito);
            this.tempHito = null;
            this.view.updateUI();
            
        }
    }
    
    private void actionRoad(String paramString) {
        
        if(this.textMenuEdit.equals(paramString)){
            roadModel editRoad = this.tempRoad;
            this.tempRoad = null;
            this.mainController.editRoad(editRoad);
            
        }
        
        else if(this.textMenuDelete.equals(paramString)){
            this.deleteRoad(this.tempRoad);
            this.tempRoad = null;
            this.view.updateUI();
            
        }
    }
    
    private void actionEditArea(mouseController mouse) {
        
        
        if( this.tempHito == null && mouse.getState() == mouseType.mouseClicked && mouse.isClickLeft()){
            mouse.setClickLeft(false);
            this.tempHito = this.graph.findHito(mouse.getPoint());
            
            if(this.tempHito != null && this.tempHito.getType() == hitoType.AREA){
                this.tempHito.setSeeVertices(true);
            }
        }
        
        else if(this.tempHito != null && mouse.getState() == mouseType.mouseDragged && mouse.isPressedLeft()){
            
            this.tempHito.moveVertice(mouse.getPoint());
        }
        
        else if(this.tempHito != null && !mouse.isPressedLeft()){
            
            this.tempHito.setSelectVertex(null);
        }
        
        else if(this.tempHito != null &&  mouse.getState() == mouseType.mouseClicked && mouse.isClickRigth()){
            mouse.setClickRigth(false);
            this.tempHito.setSeeVertices(false);
            this.tempHito = null;
            
        }
        
        this.view.repaint();
        
    }
    
    public void deleteHito(hitoModel oldHito){        
        graph.removeHito(oldHito);
        this.view.repaint();      
        
    }
    
    public void deleteRoad(roadModel oldRoad){        
        graph.removeRoad(oldRoad);
        this.view.repaint();      
        
    }

    public void actionMoveLayer(mouseController mouse){
        if( mouse.isPressedRigth() ){
            if(mouse.getState() == mouseType.mousePressed || lastPoint == null){
                lastPoint = new Point(mouse.getPoint());
            }
        }
        int moveX = (this.view.getLocation().x +  (mouse.getPoint().x - lastPoint.x));
        int moveY = (this.view.getLocation().y +  (mouse.getPoint().y - lastPoint.y));
        
        Dimension limint = this.view.getParent().getSize();
    }

    public void editElement(hitoModel hitoEdit) {
        
        // validamos que el hito no llegue null
        if(hitoEdit == null){
            System.out.println("la funcion editELement el paramentro hitoEdit es null");
            return;
        }
        
        hitoModel thereHito = graph.getHito(hitoEdit.getId());
        if(thereHito == null){
            System.out.println("No se encontro hito en la funcion editElement");
            return;
        }
        
        thereHito.setType(hitoEdit.getType());
        thereHito.setName(hitoEdit.getName());
        thereHito.setAlias(hitoEdit.getAlias());
        thereHito.setColorEdge(hitoEdit.getColorEdge());
        thereHito.setColorFill(hitoEdit.getColorFill());
    }

    public void editElement(roadModel roadEdit) {
        // validamos que el hito no llegue null
        if(roadEdit == null){
            System.out.println("la funcion editELement el paramentro roadEdit es null");
            return;
        }
        
        roadModel thereRoad = graph.getRoad(roadEdit.getId());
        if(thereRoad == null){
            System.out.println("No se encontro road en la funcion editElement");
            return;
        }
        
        thereRoad.setType(roadEdit.getType());
        thereRoad.setName(roadEdit.getName());
        thereRoad.setAlias(roadEdit.getAlias());
        thereRoad.setColor(roadEdit.getColor());
    }

    public int getId() {
        return this.id;
    }
    
    public void setId(int id){
        this.id = id;
    }
    
    public int getParentId() {
        return this.parentId;
    }
    
    public void setParentId(int parentId){
        this.parentId = parentId;
    }
    
}
